// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/abi"
	"unsafe"
)

type mOS struct {
	initialized bool
	mutex       pthreadmutex
	cond        pthreadcond
	count       int
}

func unimplemented(name string) {
	println(name, "not implemented")
	*(*int)(unsafe.Pointer(uintptr(1231))) = 1231
}

// go:nosplit 
func semacreate(mp *m) {
	if mp.initialized {
		return
	}
	mp.initialized = true
	if err := pthread_mutex_init(&mp.mutex, nil); err != 0 {
		throw("pthread_mutex_init")
	}
	if err := pthread_cond_init(&mp.cond, nil); err != 0 {
		throw("pthread_cond_init")
	}
}

// go:nosplit 
func semasleep(ns int64) int32 {
	var start int64
	if ns >= 0 {
		start = nanotime()
	}
	mp := getg().m
	pthread_mutex_lock(&mp.mutex)
	for {
		if mp.count > 0 {
			mp.count--
			pthread_mutex_unlock(&mp.mutex)
			return 0
		}
		if ns >= 0 {
			spent := nanotime() - start
			if spent >= ns {
				pthread_mutex_unlock(&mp.mutex)
				return -1
			}
			var t timespec
			t.setNsec(ns - spent)
			err := pthread_cond_timedwait_relative_np(&mp.cond, &mp.mutex, &t)
			if err == _ETIMEDOUT {
				pthread_mutex_unlock(&mp.mutex)
				return -1
			}
		} else {
			pthread_cond_wait(&mp.cond, &mp.mutex)
		}
	}
}

// go:nosplit 
func semawakeup(mp *m) {
	pthread_mutex_lock(&mp.mutex)
	mp.count++
	if mp.count > 0 {
		pthread_cond_signal(&mp.cond)
	}
	pthread_mutex_unlock(&mp.mutex)
}

// sigNote函数使用的读写文件描述符。
var sigNoteRead, sigNoteWrite int32

// sigNoteSetup初始化异步信号安全注释。
// 
// Darwin上notes的当前实现不是异步信号安全的，
// 因为函数pthread_mutex_lock、pthread_cond_signal和
// pthread_mutex_unlock（由semawakeup调用）不是异步信号安全的。
// 只有一种情况我们需要从信号中唤醒一个注释
// 处理程序：sigsend函数。信号处理程序代码不需要
// notes的所有功能：它不需要执行定时等待。
// 这是notes的一个单独实现，基于管道，不支持定时等待，但异步信号安全。
func sigNoteSetup(*note) {
	if sigNoteRead != 0 || sigNoteWrite != 0 {
		throw("duplicate sigNoteSetup")
	}
	var errno int32
	sigNoteRead, sigNoteWrite, errno = pipe()
	if errno != 0 {
		throw("pipe failed")
	}
	closeonexec(sigNoteRead)
	closeonexec(sigNoteWrite)

	// 使管道的写入端不阻塞，这样，如果管道
	// 缓冲区已满，我们就不会在信号处理程序中阻塞。
	// 保留管道阻塞的读取端，以便我们在sigNoteSleep中阻塞
	// 。
	setNonblock(sigNoteWrite)
}

// sigNoteWakeup唤醒在sigNoteSetup创建的便笺上睡觉的线程。
func sigNoteWakeup(*note) {
	var b byte
	write(uintptr(sigNoteWrite), unsafe.Pointer(&b), 1)
}

// sigNoteSleep等待sigNoteSetup创建的便笺被唤醒。
func sigNoteSleep(*note) {
	for {
		var b byte
		entersyscallblock()
		n := read(sigNoteRead, unsafe.Pointer(&b), 1)
		exitsyscall()
		if n != -_EINTR {
			return
		}
	}
}

// BSD线程接口。
func osinit() {
	// pthread_create延迟到goenvs的末尾，以便我们可以首先查看环境。

	ncpu = getncpu()
	physPageSize = getPageSize()
}

func sysctlbynameInt32(name []byte) (int32, int32) {
	out := int32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	return ret, out
}

// go:linkname internal\u cpu\u getsysctlbyname internal/cpu.getsysctlbyname 
func internal_cpu_getsysctlbyname(name []byte) (int32, int32) {
	return sysctlbynameInt32(name)
}

const (
	_CTL_HW      = 6
	_HW_NCPU     = 3
	_HW_PAGESIZE = 7
)

func getncpu() int32 {
	// 使用sysctl获取hw.ncpu。
	mib := [2]uint32{_CTL_HW, _HW_NCPU}
	out := uint32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret >= 0 && int32(out) > 0 {
		return int32(out)
	}
	return 1
}

func getPageSize() uintptr {
	// 使用sysctl获取hw.pagesize。
	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
	out := uint32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret >= 0 && int32(out) > 0 {
		return uintptr(out)
	}
	return 0
}

var urandom_dev = []byte("/dev/urandom\x00")

// go:nosplit 
func getRandomData(r []byte) {
	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
	closefd(fd)
	extendRandom(r, int(n))
}

func goenvs() {
	goenvs_unix()
}

// 可能在m.p==nil的情况下运行，因此不允许使用写屏障。
// go:nowritebarrierrec 
func newosproc(mp *m) {
	stk := unsafe.Pointer(mp.g0.stack.hi)
	if false {
		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
	}

	// 初始化属性对象。
	var attr pthreadattr
	var err int32
	err = pthread_attr_init(&attr)
	if err != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	// 找出我们自己的堆栈保护的操作系统堆栈大小。
	var stacksize uintptr
	if pthread_attr_getstacksize(&attr, &stacksize) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}
	mp.g0.stack.hi = stacksize // 对于mstart 

	// 告诉pthread库我们不会加入此线程。
	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	// 最后，创建线程。它从mstart_stub开始，它执行一些低级的
	// 设置，然后调用mstart。
	var oset sigset
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	err = pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp))
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if err != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}
}

// 从pthread_create中粘贴调用mstart的代码。
func mstart_stub()

// newosproc0是newosproc的一个版本，可以在初始化运行时
// 之前调用该版本。
// 
// 此函数在初始化后使用不安全，因为它不会将M作为fnarg传递。
// 
// go:nosplit 
func newosproc0(stacksize uintptr, fn uintptr) {
	// 初始化属性对象。
	var attr pthreadattr
	var err int32
	err = pthread_attr_init(&attr)
	if err != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	// 调用者传入建议的堆栈大小，
	// 从我们自己分配堆栈和线程开始，
	// 不带libpthread。现在我们使用libpthread，
	// 我们使用操作系统默认堆栈大小而不是建议。
	// 找出我们自己的堆栈保护的堆栈大小。
	if pthread_attr_getstacksize(&attr, &stacksize) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}
	g0.stack.hi = stacksize // 对于mstart 
	memstats.stacks_sys.add(int64(stacksize))

	// 告诉pthread库我们不会加入此线程。
	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	// 最后，创建线程。它从mstart_stub开始，它执行一些低级的
	// 设置，然后调用mstart。调用
	var oset sigset
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	err = pthread_create(&attr, fn, nil)
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if err != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}
}

var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
var failthreadcreate = []byte("runtime: failed to create new OS thread\n")

// 对使用
// /-buildmode=c-archive或-buildmode=c-shared生成的Go代码进行同步初始化。
// Go运行时均未初始化。
// go:nosplit 
// go:nowritebarrierrec 
func libpreinit() {
	initsig(true)
}

// 调用以初始化新m（包括引导程序m）。
// 在父线程（引导时为主线程）上调用，可以分配内存。
func mpreinit(mp *m) {
	mp.gsignal = malg(32 * 1024) // OS X希望>=8K 
	mp.gsignal.m = mp
	if GOOS == "darwin" && GOARCH == "arm64" {
		// M锁定信号堆栈，以解决内核错误，当信号
		// 到达时，如果信号堆栈未出现故障，它可能会发出信号。见第42774期。
		mlock(unsafe.Pointer(mp.gsignal.stack.hi-physPageSize), physPageSize)
	}
}

// 调用以初始化新的m（包括引导程序m）。
// 在新线程上调用，无法分配内存。
func minit() {
	// iOS不支持备用信号堆栈。
	// 信号处理器直接处理。
	if !(GOOS == "ios" && GOARCH == "arm64") {
		minitSignalStack()
	}
	minitSignalMask()
	getg().m.procid = uint64(pthread_self())
}

// 从drop调用以撤消minit的效果。
// go:nosplit 
func unminit() {
	// iOS不支持备用信号堆栈。
	// 见minit。
	if !(GOOS == "ios" && GOARCH == "arm64") {
		unminitSignals()
	}
}

// 从exitm调用，而不是从drop调用，以撤消线程拥有的
// minit、semacreate或其他地方的资源的效果。调用此命令后不要使用锁。
func mdestroy(mp *m) {
}

// go:nosplit-
func osyield_no_g() {
	usleep_no_g(1)
}

// go:nosplit-
func osyield() {
	usleep(1)
}

const (
	_NSIG        = 32
	_SI_USER     = 0 /* empirically true, but not what headers say */
	_SIG_BLOCK   = 1
	_SIG_UNBLOCK = 2
	_SIG_SETMASK = 3
	_SS_DISABLE  = 4
)

// /extern-SigTabTT-runtime·sigtab[]；

type sigset uint32

var sigset_all = ^sigset(0)

// go:nosplit 
// go:nowritebarrierrec 
func setsig(i uint32, fn uintptr) {
	var sa usigactiont
	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
	sa.sa_mask = ^uint32(0)
	if fn == funcPC(sighandler) { // funcPC（sighandler）匹配signal_unix中的调用者。go 
		if iscgo {
			fn = abi.FuncPCABI0(cgoSigtramp)
		} else {
			fn = abi.FuncPCABI0(sigtramp)
		}
	}
	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
	sigaction(i, &sa, nil)
}

// sigtramp是接收到信号时来自libc的回调。
// 它是用C调用约定调用的。
func sigtramp()
func cgoSigtramp()

// go:nosplit 
// go:NOWRITEBRIERREC 
func setsigstack(i uint32) {
	var osa usigactiont
	sigaction(i, nil, &osa)
	handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
	if osa.sa_flags&_SA_ONSTACK != 0 {
		return
	}
	var sa usigactiont
	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = handler
	sa.sa_mask = osa.sa_mask
	sa.sa_flags = osa.sa_flags | _SA_ONSTACK
	sigaction(i, &sa, nil)
}

// go:nosplit 
// go:NOWRITEBRIERREC 
func getsig(i uint32) uintptr {
	var sa usigactiont
	sigaction(i, nil, &sa)
	return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
}

// setSignaltstackSP设置堆栈的ss_sp字段。
// go:nosplit 
func setSignalstackSP(s *stackt, sp uintptr) {
	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
}

// go:nosplit 
// go:nowritebarrierrec 
func sigaddset(mask *sigset, i int) {
	*mask |= 1 << (uint32(i) - 1)
}

func sigdelset(mask *sigset, i int) {
	*mask &^= 1 << (uint32(i) - 1)
}

// go:linkname executablePath os.executablePath 
var executablePath string

func sysargs(argc int32, argv **byte) {
	// 跳过argv、env，第一个字符串将是路径
	n := argc + 1
	for argv_index(argv, n) != nil {
		n++
	}
	executablePath = gostringnocopy(argv_index(argv, n+1))

	const prefix = "executable_path="
	if len(executablePath) > len(prefix) && executablePath[:len(prefix)] == prefix {
		executablePath = executablePath[len(prefix):]
	}
}

func signalM(mp *m, sig int) {
	pthread_kill(pthread(mp.procid), uint32(sig))
}
